package com.zzh.lib.recorder.pcm;

import android.annotation.SuppressLint;
import android.media.AudioRecord;
import android.media.audiofx.NoiseSuppressor;
import android.os.Process;
import android.util.Log;

import com.zzh.lib.recorder.HMediaRecorder;
import com.zzh.lib.recorder.HMediaRecorderParams;
import com.zzh.lib.recorder.def.IRecorder;
import com.zzh.lib.recorder.mp3.PCMFormat;
import com.zzh.lib.recorder.utils.HLog;
import com.zzh.lib.recorder.utils.PcmHeader;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by ZZH on 2022/12/28
 *
 * @Date: 2023/01/05 9:30
 * @Email: zzh_hz@126.com
 * @QQ: 1299234582
 * @Author: zzh
 * @Description: PCM 音频格式
 */
public class PCMRecorder implements IRecorder {
    /**
     * 自定义 每160帧作为一个周期，通知一下需要进行编码
     */
    private static final int FRAME_COUNT = 160;
    private AudioRecord mAudioRecord = null;
    private int mBufferSize;
    private byte[] mPCMBuffer;
    private boolean mIsRecording = false;
    private File mRecordFile;
    private HMediaRecorderParams mRecorderParams;
    BufferedOutputStream bos;

    /**
     * Default constructor. Setup recorder with default sampling rate 1 channel,
     * 16 bits pcm
     *
     * @param recordFile target file
     */
    public PCMRecorder(File recordFile) {
        mRecordFile = recordFile;
        mRecorderParams = HMediaRecorderParams.buildDefaultPCM();
    }

    public PCMRecorder(HMediaRecorderParams params) {
        mRecorderParams = params;
    }

    public PCMRecorder() {
        mRecorderParams = HMediaRecorderParams.buildDefaultPCM();
    }

    private long startTime = 0;

    /**
     * Start recording. Create an encoding thread. Start record from this
     * thread.
     *
     * @throws IOException initAudioRecorder throws
     */
    @SuppressLint("MissingPermission")
    public void start() throws IOException {
        if (mIsRecording) {
            return;
        }
        //开始时间
        startTime = System.currentTimeMillis();
        mIsRecording = true; // 提早，防止init或startRecording被多次调用
        if (mRecorderParams != null) {
            mRecorderParams.getOnRecorderProcessListener().onBeforeInitAudioRecord(this);
        }
        initAudioRecorder();
        if (mRecorderParams != null) {
            mRecorderParams.getOnRecorderProcessListener().onStart(this);
        }
        mAudioRecord.startRecording();
        new Thread() {
            @Override
            public void run() {
                //设置线程权限
                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);

                while (mIsRecording) {
                    int readSize = mAudioRecord.read(mPCMBuffer, 0, mBufferSize);
                    if (readSize > 0) {
                        if (mRecorderParams.isSave2File()) {
                            try {
                                if (bos != null) {
                                    bos.write(mPCMBuffer, 0, readSize);
                                }
                            } catch (IOException e) {
                                onExceptionError(e);
                            }
                        }
                        //mEncodeThread.addTask(mPCMBuffer, readSize);
                        //保存到文件中，回调此流
                        if (mRecorderParams.getOnRecorderProcessListener() != null) {
                            mRecorderParams.getOnRecorderProcessListener().onProcess(PCMRecorder.this, mPCMBuffer, readSize);
                            mVolume = calculateRealVolume(mPCMBuffer);
                            mRecorderParams.getOnRecorderProcessListener().volumeChange(mVolume);
                        }
                    }
                }
            }


        }.start();
    }

    /**
     * 此计算方法来自samsung开发范例
     *
     * @param buffer buffer 【由short[] 转换成byte[]】
     */
    public double calculateRealVolume(byte[] buffer) {
        double sumVolume = 0.0;

        double avgVolume = 0.0;

        for (int i = 0; i < buffer.length; i += 2) {
            int v1 = buffer[i] & 0xFF;

            int v2 = buffer[i + 1] & 0xFF;

            int temp = v1 + (v2 << 8);// 小端

            if (temp >= 0x8000) {
                temp = 0xffff - temp;

            }

            sumVolume += Math.abs(temp);

        }

        avgVolume = sumVolume / buffer.length / 2;

        return Math.log10(1 + avgVolume) * 10;
    }

    private void onExceptionError(IOException e) {
        if (mRecorderParams != null) {
            mRecorderParams.getOnExceptionCallback().onException(e);
        }
    }

    @Override
    public void start(File file) {
        if (mRecorderParams.isSave2File()) {
            this.mRecordFile = file;

            if (file == null) {
                throw new NullPointerException("录制文件参数file 不能为空");
            }
            HLog.isMp3Log("MP3录制路径：" + mRecordFile.getAbsolutePath());
        }
        try {
            start();
        } catch (IOException e) {
            Log.e("pcm", e.getMessage());
        }

    }

    @Override
    public void pause() {
        mIsRecording = false;
    }

    @Override
    public void resume() {
        mIsRecording = true;

    }

    private double mVolume;

    /**
     * 获取真实的音量。 [算法来自三星]
     *
     * @return 真实音量
     */
    public double getRealVolume() {
        return mVolume;
    }

    /**
     * 获取相对音量。 超过最大值时取最大值。
     *
     * @return 音量
     */
    public double getVolume() {
        if (mVolume >= MAX_VOLUME) {
            return MAX_VOLUME;
        }
        return mVolume;
    }

    private static final int MAX_VOLUME = 2000;

    /**
     * 根据资料假定的最大值。 实测时有时超过此值。
     *
     * @return 最大音量值。
     */
    public int getMaxVolume() {
        return MAX_VOLUME;
    }

    public void stop() {
        mIsRecording = false;
        try {
            if (mRecorderParams.isSave2File() && bos != null) {
                bos.flush();
                if (mRecorderParams.isFileFormatWave()) {
                    PcmHeader.updateWavHeader(mRecordFile);
                }
            }
        } catch (IOException e) {
            onExceptionError(e);
        }
        //回调结果
        if (mRecorderParams != null) {
            long time = System.currentTimeMillis() - startTime;
            mRecorderParams.getOnRecorderProcessListener().onEnd(time);
        }
        // 停止
        if (mAudioRecord != null) {
            mAudioRecord.stop();
        }

    }

    @Override
    public void release() {
        try {
            if (mRecorderParams.isSave2File() && bos != null) {
                bos.flush();
                bos.close();
                if (mRecorderParams.isFileFormatWave()) {
                    PcmHeader.updateWavHeader(mRecordFile);
                }
            }

        } catch (IOException e) {
            onExceptionError(e);
        }
        if (mAudioRecord != null) {
            mAudioRecord.release();
        }
        bos = null;
        mAudioRecord = null;
        mRecordFile = null;
    }

    @Override
    public void reset() {

    }

    @Override
    public boolean isRecorder() {
        return mIsRecording;
    }

    @Override
    public File getRecordFile() {
        return mRecordFile;
    }

    @Override
    public PCMRecorder setRecordFile(File file) {
        this.mRecordFile = file;
        return this;
    }

    @Override
    public HMediaRecorder.State getState() {
        if (mIsRecording) {
            return HMediaRecorder.State.Recording;
        } else {
            return HMediaRecorder.State.Idle;
        }
    }

    /**
     * 初始化 AudioRecord 对象
     */
    @SuppressLint("MissingPermission")
    private void initAudioRecorder() throws IOException {
        if (mRecorderParams.isSave2File()) {
            if (mRecordFile == null) {
                String mimeType = mRecorderParams.isFileFormatWave() ? ".wav" : ".pcm";
                mRecordFile = new File(mRecorderParams.getFileSaveDir(), System.currentTimeMillis() + mimeType);
                boolean newFile = mRecordFile.createNewFile();
                HLog.isMRLog("---------创建文件结果：" + newFile);
            }
            //写入文件
            FileOutputStream out = new FileOutputStream(mRecordFile, true);
            bos = new BufferedOutputStream(out);
            if (mRecorderParams.isFileFormatWave()) {
                PcmHeader.saveWaveDelayTotalLength(bos, mRecorderParams.getAudioSamplingRate(), mRecorderParams.getAudioChannels(), PCMFormat.valueOf(mRecorderParams.getOutputFormat()).getAudioFormat());
            }
        }

        if (mRecorderParams.getBufferSize() > 0) {
            mBufferSize = mRecorderParams.getBufferSize();
        } else {
            mBufferSize = AudioRecord.getMinBufferSize(mRecorderParams.getAudioSamplingRate(), mRecorderParams.getAudioChannels(), PCMFormat.valueOf(mRecorderParams.getOutputFormat()).getAudioFormat());


            int bytesPerFrame = PCMFormat.valueOf(mRecorderParams.getOutputFormat()).getBytesPerFrame();
            /* Get number of samples. Calculate the buffer size
             * (round up to the factor of given frame size)
             * 使能被整除，方便下面的周期性通知
             * */
            int frameSize = mBufferSize / bytesPerFrame;
            if (frameSize % FRAME_COUNT != 0) {
                frameSize += (FRAME_COUNT - frameSize % FRAME_COUNT);
                mBufferSize = frameSize * bytesPerFrame;
            }
        }

        /* Setup audio recorder */
        mAudioRecord = new AudioRecord(mRecorderParams.getAudioSource(), mRecorderParams.getAudioSamplingRate(),
                mRecorderParams.getAudioChannels(), PCMFormat.valueOf(mRecorderParams.getOutputFormat()).getAudioFormat(), mBufferSize);

        Log.e("----", "-------: status: " + mAudioRecord.getState());

        if (mRecorderParams != null && mRecorderParams.isDenoise()) {
            if (NoiseSuppressor.isAvailable()) {
                NoiseSuppressor noiseSuppressor = NoiseSuppressor.create(mAudioRecord.getAudioSessionId());
                if (noiseSuppressor != null) {
                    noiseSuppressor.setEnabled(true);
                }
            }
        }
        mPCMBuffer = new byte[mBufferSize];

        // Create and run thread used to encode data
        // The thread will

        mAudioRecord.setPositionNotificationPeriod(FRAME_COUNT);
    }

    public void setRecorderParams(HMediaRecorderParams recorderParams) {
        if (recorderParams != null) {
            mRecorderParams = recorderParams;
        } else {
            mRecorderParams = HMediaRecorderParams.buildDefaultPCM();
        }

    }

    public HMediaRecorderParams getRecorderParams() {
        return mRecorderParams;
    }

    public AudioRecord getAudioRecord() {
        return mAudioRecord;
    }
}